#ifndef __shell_kenny_dkfjiwoo3w3__
#define __shell_kenny_dkfjiwoo3w3__

#include <unistd.h>
#include <sys/wait.h>

#include <hude/utils/pty.hpp>

#include "brick/brick.hpp"

namespace ha
{
    namespace brick
    {
        /*********************************************************************
         * @brief shell进程
         */
        class shell_t :
            public _brick_aio_t< shell_t >
        {
        public:
            shell_t( aio_t aio, const json_t& args ) : _brick_aio_t< shell_t >( aio ), pid_( 0 )
            {
                pipe_to_child_[0] = -1;
                pipe_to_child_[1] = -1;
                pipe_from_child_[0] = -1;
                pipe_from_child_[1] = -1;

                if( settings( args ) )
                {
                    this->mount( pipe_from_child_[0] );
                }
            }

            ~shell_t()
            {
                this->unmount( pipe_from_child_[0] );

                close();
            }

            int recv( void* p, size_t n ) { return ( pid_ > 0 )? ::read( pipe_from_child_[0], p, n ) : 0; }

            int send( const void* p, size_t n ) override { return ( pid_ > 0 )? ::write( pipe_to_child_[1], p, n ) : 0; }

            void close() override { close_child(); close_pipes( false ); }

            bool settings( const json_t& args ) override;

            bool ok() const { return pid_ > 0; }

            const std::string& cmd() const { return cmd_; }
            const std::list< std::string >& args() const { return args_; }

        private:
            bool fork( char* [] );
            void close_child();
            void close_pipes( bool child );

        private:
            std::string              cmd_;
            std::list< std::string > args_;
            pid_t                    pid_;
            int                      pipe_to_child_[ 2 ];
            int                      pipe_from_child_[ 2 ];
        };

        /*********************************************************************
         * @brief 设置shell参数
         */
        inline bool shell_t::settings( const json_t& args )
        {
            size_t siz = 0U;

            {
                auto _cmd  = hude::utils::json_path_t::value< std::string >( args, "cmd" );
                auto _args = hude::utils::json_path_t::value< std::list< std::string > >( args, "args" );

                if( ! _cmd )
                {
                    llog_err( "<brick>", "ERROR: invaild command." );

                    return false;
                }

                cmd_ = *_cmd;
                args_ = *_args;

                siz = args_.size();
            }

            std::unique_ptr< char*[] > cmds( new char*[siz + 2U] );

            {
                cmd_.push_back( '\0' );
                cmds[ 0 ] = (char*)(void*)&( cmd_[ 0 ] );

                int index = 1;

                for( auto itr = args_.begin(); itr != args_.end(); ++itr, ++index )
                {
                    itr->push_back( '\0' );

                    cmds[ index ] = (char*)(void*)&( ( *itr )[ 0 ] );
                }

                cmds[index] = nullptr;
            }

            return fork( cmds.get() );
        }

        /*********************************************************************
         * @brief 关闭shell子进程
         */
        inline void shell_t::close_child()
        {
            if( pid_ > 0 )
            {
                ::kill( pid_, SIGKILL );

                ::waitpid( pid_, nullptr, 0 );

                pid_ = 0;
            }
        }

        /*********************************************************************
         * @brief 关闭shell所有管道
         */
        void shell_t::close_pipes( bool child )
        {
            int* pin  = &( child? pipe_to_child_[0]   : pipe_from_child_[0] );
            int* pout = &( child? pipe_from_child_[1] : pipe_to_child_[1]   );

            if( *pin  > 0 ) ::close( *pin  );
            if( *pout > 0 ) ::close( *pout );

            pipe_to_child_[ 0 ]   = -1;
            pipe_to_child_[ 1 ]   = -1;
            pipe_from_child_[ 0 ] = -1;
            pipe_from_child_[ 1 ] = -1;
        }

        /*********************************************************************
         * @brief 启动shell子进程
         */
        inline bool shell_t::fork( char* args[] )
        {
            close_child();
            close_pipes( false );

            if( 0 != ::pipe( pipe_to_child_ ) ||
                0 != ::pipe( pipe_from_child_ ) )
            {
                llog_err( "<brick>", "ERROR: create pipe." );

                return false;
            }

            set_noblock( pipe_to_child_[0] );
            set_noblock( pipe_from_child_[0] );

            pid_ = ::fork();

            if( pid_ == -1 )
            {
                close_pipes( false );

                llog_err( "<brick>", "ERROR: fork child." );

                return false;
            } else if( pid_ == 0 ) {
                ::dup2( pipe_to_child_[0],   STDIN_FILENO  );
                ::dup2( pipe_from_child_[1], STDOUT_FILENO );

                close_pipes( true );

                std::this_thread::sleep_for( std::chrono::milliseconds( 100U ) );

                auto ret = ::execvp( args[0], args );

                close_pipes( false );

                ::exit( ret );

                return false;
            } else {
                ::close( pipe_to_child_[0]   );
                ::close( pipe_from_child_[1] );

                std::thread( [=]{
                    ::waitpid( pid_, nullptr, 0 );
                    this->pid_ = 0;

                    llog_war( "<brick>", "\t <brick> shell exited: %s", cmd_.c_str() );
                } ).detach();

                return true;
            }
        }


    } //namespace brick
} //namespace ha

#endif //__shell_kenny_dkfjiwoo3w3__

